home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 2
/
Nebula Two.iso
/
SourceCode
/
MiniExamples
/
AppKit
/
WhatsUpDoc
/
Document.m
< prev
next >
Wrap
Text File
|
1995-06-12
|
8KB
|
345 lines
/* Document.m
* Purpose: Initializes, loads, archives and frees a single
* document. The template for the document is located in
* Document.nib, which is loaded by this class each time
* a new document is created. The windows are tiled using
* the newLocation() function.
*
* You may freely copy, distribute, and reuse the code in this example.
* NeXT disclaims any warranty of any kind, expressed or implied, as to its
* fitness for any particular use.
*
* Written by: R. Dunbar Poor
* Created: 28/April/1991
*
*/
#import "Document.h"
#import "DocController.h"
#import <appkit/appkit.h>
#import <objc/hashtable.h> /* for NXCopyStringBufferFromZone */
#import <sys/param.h>
#import <strings.h> /* for strncpy() */
#import <stdlib.h>
@interface Document(DocumentPrivate)
- _saveWithNewName:(BOOL)doNewName retainNewName:(BOOL)doRetain;
- _write:(const char *)filename;
- _read:(const char *)filename;
@end
static void newLocation(NXPoint *p)
/*
* This method computes a new location for each new window created.
*/
{
static count = 0;
p->x += (20.0 * count);
p->y -= (25.0 * count);
count = (count > 10)? 0 : count+1;
}
@implementation Document
- init
/*
* The default initialization is simply to open a new (empty) document.
*/
{
return [self initFromFile:NULL];
}
- initFromFile:(const char *)filename
/*
* Read a document and initialize it from a file.
*/
{
[super init];
[NXApp loadNibSection:"Document.nib" owner:self withNames:NO];
[self loadFromFile:filename];
if (!filename) {
NXRect theFrame;
/* If its a new document, generate a new position for it */
[docWindow getFrame:&theFrame];
newLocation(&theFrame.origin);
[docWindow moveTo:theFrame.origin.x :theFrame.origin.y];
}
[docWindow makeKeyAndOrderFront:self];
/*
* I'm not sure why windowDidBecomeMain isn't called as a result of
* makeKeyAndOrderFront. At any rate, we want new windows to become
* the "active" document.
*/
[self windowDidBecomeMain:self];
return self;
}
- loadFromFile:(const char *)filename
/*
* Come here with all the nib objects instantiated. We initialize any
* application-specific state from the contents from the given filename,
* and finish up any initialization.
*/
{
if (filename) {
if (![self _read:filename]) {
[self free]; /* couldn't load document file */
return nil;
}
}
/*
* do some common setup.
*/
[self setDocumentName:filename];
[self setDocEdited:NO];
return self;
}
- free
{
/* tell the controller that we can no longer be the active doucment */
[[NXApp delegate] unsetActiveDocument:self];
if (name) free(name);
[docWindow free];
return [super free];
}
- (const char *)message
{
return [docContents stringValue];
}
- activateDocument:sender
{
[statusField setStringValue:"Active..."];
return self;
}
- deactivateDocument:sender
{
[statusField setStringValue:"Inactive..."];
return self;
}
- hideDocument:sender
{
[docWindow orderOut:sender];
return self;
}
- setDocEdited:(BOOL)edited
{
[docWindow setDocEdited:edited];
/*
* The following is a hack to tell the controller to update the
* Revert menu cell. Unfortunately, it does lots of work besides
* that...
*/
if ([[NXApp delegate] activeDocument] == self) {
[[NXApp delegate] setActiveDocument:self];
}
return self;
}
- (BOOL)isDocEdited
{
return [docWindow isDocEdited];
}
- setDocumentName:(const char *)newName
{
/*
* If we are passing 'name' itself as the newName argument, as will happen
* in the revert: method, we don't want to modify it. In particular, we
* better not call free() on it and then try to copy it back to itself (which
* was a bug I had for a while.)
*/
if (newName != name) {
/* name isn't the same as newName, so it is safe to free name now */
if (name) free(name);
if (newName) {
name = NXCopyStringBuffer(newName);
} else {
name = NULL;
}
}
if (name) {
[docWindow setTitleAsFilename:name];
} else {
[docWindow setTitleAsFilename:"Untitled Document"];
}
return self;
}
- save:sender
{
if ([self isDocEdited] || !name) {
[self _saveWithNewName:NO retainNewName:YES];
}
return self;
}
- saveAs:sender { return [self _saveWithNewName:YES retainNewName:YES]; }
- saveTo:sender { return [self _saveWithNewName:YES retainNewName:NO]; }
- revert:sender
{
int choice;
if ([self isDocEdited]) {
choice = NXRunAlertPanel(
"Revert",
"Discard changes to the document?",
"Revert",
"Cancel",
NULL);
switch (choice) {
case NX_ALERTDEFAULT:
[self loadFromFile:name];
break;
case NX_ALERTOTHER:
return nil;
}
}
return self;
}
- close:sender
{
[docWindow performClose:self];
return sender;
}
- dirty:sender
{
[self setDocEdited:YES];
return [self windowDidBecomeMain:self];
}
- checkForEdited:sender
/*
* If the document is edited, give the user a chance to save the
* document. Returns nil if they want to cancel.
*/
{
int choice;
if ([self isDocEdited]) {
[docWindow makeKeyAndOrderFront:self];
choice = NXRunAlertPanel(
"Close",
"Save changes to %s?",
"Save", /* NX_ALERTDEFAULT */
"Don't Save", /* NX_ALERTALTERNATE */
"Cancel", /* NX_ALERTOTHER */
(name)?name:"Untitled Document");
switch (choice) {
case NX_ALERTALTERNATE:
break;
case NX_ALERTDEFAULT:
[self save:nil];
break;
case NX_ALERTOTHER:
return nil;
}
}
return self;
}
- windowDidBecomeMain:sender
{
[[NXApp delegate] setActiveDocument:self];
return [self activateDocument:sender];
}
- windowDidResignMain:sender
{
return [self deactivateDocument:sender];
}
- windowWillClose:sender
{
if (![self checkForEdited:sender]) return nil;
/* make the document disavow any knowledge of the window */
[docWindow setDelegate:nil];
docWindow = nil;
/* The document can't live now that its window is gone... */
[self free];
return sender;
}
/*
* All varieties of save go through this routine. It covers all the cases
* of running the Save Panel and retaining the name chosen.
*/
- _saveWithNewName:(BOOL)doNewName retainNewName:(BOOL)doRetain
{
id savePanel;
const char *saveName; /* filename to save into */
if (!name || doNewName) {
/* saveAs or saveTo */
savePanel = [SavePanel new];
[savePanel setRequiredFileType:DOCUMENT_TYPE];
if ([savePanel runModalForDirectory:NULL file:name]) {
saveName = [savePanel filename];
} else {
/* aborted out? */
return self;
}
} else {
/* ordinary Save */
saveName = name;
}
[self _write:saveName];
/* update the document name if requested */
if (doRetain) {
[self setDocumentName:saveName];
[docWindow setDocEdited:NO];
}
return self;
}
- _write:(const char *)filename
{
NXTypedStream *ts;
NXRect theFrame;
const char *contents;
ts = NXOpenTypedStreamForFile(filename, NX_WRITEONLY);
if (!ts) return self;
[docWindow getFrame:&theFrame];
NXWriteRect(ts, &theFrame);
contents = [docContents stringValue];
NXWriteType(ts, "*", &contents);
NXCloseTypedStream(ts);
return self;
}
- _read:(const char *)filename
{
NXTypedStream *ts;
NXRect theFrame;
char *contents;
ts = NXOpenTypedStreamForFile(filename, NX_READONLY);
if (!ts)
return self;
NXReadRect(ts, &theFrame);
[docWindow placeWindowAndDisplay:&theFrame];
NXReadType(ts, "*", &contents);
[docContents setStringValue:contents];
NXCloseTypedStream(ts);
return self;
}
@end